package Controller;

import Controller.EstandarxDLG;
import Controller.*;
import Controller.GraficadorDLG;
/**
 * x=Q(u)
 */
public class MetodoMaxVerosimilitud extends AlgoritmoGeneticoMaxVer{
	/**
	 * se usa para calculos especificos de DLG
	 */
	private DLG dlg;
	/**
	 * usado por el metodo numerico para hallar U
	 */
	private static double TOLERANCIA_U;
	/**
	 * muestra[] heredado. x[] es la muestra ordenada sin repetidos
	 * con su respectiva distribucion Fx.(p es la distrib para unos lambdas)
	 */
	double x[],Fx[],u[],p[];

	public MetodoMaxVerosimilitud(){
		super();
		TOLERANCIA_U=0.1;
		dlg=new DLG();
		DLG.TOLERANCIA_U=TOLERANCIA_U;
	}
	
	/**
	 * constructor
	 * @param estandarxDLG 
	 */
	public MetodoMaxVerosimilitud(EstandarxDLG estandarxDLG){
		super(estandarxDLG);
		TOLERANCIA_U=0.1;
		dlg=new DLG();
		DLG.TOLERANCIA_U=TOLERANCIA_U;
	}
	/**
	 * ejecuta el AG
	 */
	public void run() {
		ordenarMuestra();
		calcularFxConMuestra();
		super.run();
	}
	/**
	 * orden ascendente
	 */
	private void ordenarMuestra() {
		for (int i = 0; i < muestra.length-1; i++) {
			if (muestra[i] > muestra[i + 1]) {
				double  temp = muestra[i];
				muestra[i] = muestra[i+1];
				muestra[i+1] = temp;
				if (i > 0) {
					i -= 2;
				}
			}
		}			
	}
	/**
	 * Funcion de Distribucion u=Fx(x)
	 */
	private void calcularFxConMuestra() {
		//n es el # de x distintos
		int i,n=0;
		for (i=0; i < muestra.length; i++) {
			double xi=muestra[i];
			while(i < muestra.length&&muestra[i]==xi){
				i++;
			}
			i--;
			n++;
		}
		//calcula Fx y crea x
		x=new double[n];
		Fx=new double[n];
		double Fxi=0;
		for (i = 0, n = 0; i < muestra.length; i++) {
			x[n]=muestra[i];	//<--
			Fx[n]=Fxi;
			Fxi=0;
			while(i < muestra.length && muestra[i]==x[n]){//crear clases: muestra[i]-xi < epsilon
				Fxi++;i++;
			}
			i--;
			Fxi/=muestra.length;
			Fx[n]+=Fxi;			//<--
			n++;
		}
	}

	/**
	 * calcula la funcion de verosimilitud >> maximizar
	 * Define el metodo abstracto
	 */
	protected double calcularRendimientoConFxMuestral(double cromosoma[]){
		la1=dlg.la1=cromosoma[0];//la1 y la2: se usan en densidad
		la2=dlg.la2=cromosoma[1];
		la3=dlg.la3=cromosoma[2];
		la4=dlg.la4=cromosoma[3];

		double invalido = -Double.MAX_VALUE;

		if(!dlg.sonLambdasValidos())
			return invalido;

		double L=1;
		for (int i = 0; i < Fx.length; i++) {
			L *= dlg.fdensidad(Fx[i]);
		}

		if (L==Double.POSITIVE_INFINITY) {
			L=Double.MAX_VALUE;
		}
		return L;
	}

	/**
	 * calcula la funcion de verosimilitud >> maximizar
	 * Define el metodo abstracto
	 */
	protected double calcularRendimientoConFxLambdas(double cromosoma[]){
		la1=dlg.la1=cromosoma[0];//no sobra, se usa para hallar p
		la2=dlg.la2=cromosoma[1];
		la3=dlg.la3=cromosoma[2];
		la4=dlg.la4=cromosoma[3];
		
		if(p==null||p.length!=x.length){
			p=new double[x.length];
		}
		
		//funcion de verosimilitud>>>maximizar
		double L=dlg.fdensidad(p[0]);
		for (int i = 0; i < x.length; i++) {
			p[i]=dlg.calcularU(x[i]);
			L *= dlg.fdensidad(p[i]);
		}

		if (L==Double.POSITIVE_INFINITY) {
			L=Double.MAX_VALUE;
		}
		return L;
	}	
	
	/**
	 * calcula el error del cromosoma actual segun restriccion 3 y 4
	 * PRE: la1,...,la4 y p estan precalculados
	 * Define el metodo abstracto
	 * @return Double.MAX_VALUE, si no es solucion valida 
	 */
	protected double calcularErrorConFxMuestral() {
		double error1=0,error2=0;
		double divisor=0;
		for (int i = 0; i < x.length; i++) {
			divisor=la3*Math.pow(Fx[i],la3-1) + la4*Math.pow(1-Fx[i],la4-1);
			if(divisor==0){	
				return Double.MAX_VALUE;
			}
			error1 += Math.pow(Fx[i],la3-1) * (1+la3*Math.log(Fx[i])) / divisor;
			error2 += Math.pow(1-Fx[i],la4-1) * (1+la4*Math.log(1-Fx[i])) / divisor;
		}
		//como error 1 y 2 deben ser 0,el total es:
		return Math.abs(error1)+Math.abs(error2);
	}
	

	protected double calcularErrorConFxLambdas() {
		double error1=0,error2=0;
		double divisor=0;
		for (int i = 0; i < x.length; i++) {
			divisor=la3*Math.pow(p[i],la3-1) + la4*Math.pow(1-p[i],la4-1);
			if(divisor==0){	
				return Double.MAX_VALUE;
			}
			error1 += Math.pow(p[i],la3-1) * (1+la3*Math.log(p[i])) / divisor;
			error2 += Math.pow(1-p[i],la4-1) * (1+la4*Math.log(1-p[i])) / divisor;
		}
		//como error 1 y 2 deben ser 0,el total es:
		return Math.abs(error1)+Math.abs(error2);	
	}

	

	public void ajustarTOLERANCIA_P(double d) {
		DLG.TOLERANCIA_U=TOLERANCIA_U=d;		
	}
	
	public static void main(String[] args) {
		MetodoMaxVerosimilitud m=new MetodoMaxVerosimilitud();
		/*	double[] x={-0.300232159,
				-1.277683168,
				0.244257308,
				1.27647354,
				1.198350219,
				1.733133104,
				-2.18358764,
				-0.234181243,
				1.095022526,
				-1.086700649,
				-0.69020416,
				-1.690432327,
				-1.846910891,
				-0.977629497,
				-0.773507054,
				-2.117931217,
				-0.567924872,
				-0.404047569,
				0.134853053,
				-0.365492951,
				-0.32699063,
				-0.370240514,
				1.342641553,
				-0.085284455,
				-0.186157649,
				-0.513207397,
				1.972211976,
				0.865672973,
				2.375654731,
				-0.654906671};*/
		double[] x={0,1,1,2,2,2,3,3,3,3,4,4,4,4,4,5,5,5,5,5,5};
		 m.muestra=x;
		 m.generaciones=600;
		 m.poblacion=100;
		 m.probabilidadCruce=0.7;
		 m.probabilidadMutacion=0.4;
		 m.valorMax=2;
		 m.valorMin=-2;
		 m.errorMax=0.0001;
		// m.ajustarTOLERANCIA_P(0.1);

		 m.run();
		 m.la1=1;

		 GraficadorDLG d=new GraficadorDLG();
		 d.setVisible(true);
		 d.graficar("miGrafica",m.la1, m.la2, m.la3, m.la4);
	}
}